<template>
    <VueDraggable
        v-model="previewList"
        :animation="150"
        :onEnd="handleDragEnd"
        target=".arco-upload-list-type-picture-card"
    >
        <a-upload
            v-model:file-list="previewList"
            list-type="picture-card"
            action="./"
            multiple
            :limit="limit"
            :accept="accept"
            :show-upload-button="showUploadIcon"
            @change="handleChangeUpload"
            :on-before-upload="onBeforeUpload"
            @exceed-limit="handleExceedLimit"
            @preview="handlePreview"
            v-bind="$attrs"
        />
    </VueDraggable>
</template>

<script lang="ts" setup>
import { ref, computed, nextTick } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
import { FileItem, Message } from '@arco-design/web-vue'
import prview from '@/components/CImage/preview'
import { watchPausable } from '@vueuse/core'

const {
    maxSize = 5,
    limit = 5,
    valueType = 'array',
    accept = 'image/png,image/jpeg'
} = defineProps<{
    // 如果时单个图片上传的话，必须设定valueType: string
    valueType?: 'string' | 'array'
    limit?: number
    accept?: string
    maxSize?: number
}>()

const previewList = ref<FileItem[]>([])
const fileUrl = defineModel<string[] | string>({ required: true, default: [] })

/**
 * 如果时外部传进来的fileUrl，需要watch一下，并赋值给previewList，并赋值给defaultList，并重新渲染
 */
const { pause, resume } = watchPausable(
    fileUrl,
    url => {
        if (Array.isArray(url)) {
            previewList.value = url.map(urlItem => ({
                uid: Math.random().toString(),
                status: 'done',
                url: urlItem
            }))
        } else if (typeof url === 'string' && url) {
            previewList.value = [
                {
                    uid: Math.random().toString(),
                    name: 'image.png',
                    status: 'done',
                    url
                }
            ]
        }
    },
    {
        immediate: true,
        deep: true
    }
)

const showUploadIcon = computed(() => {
    const len = fileUrl.value?.length
    return valueType === 'string' ? !!fileUrl.value : len < limit
})

/**
 * 上传文件状态变化处理函数
 *
 * @param fileList 文件列表
 * @param file 当前文件
 */
const handleChangeUpload = async (fileList: FileItem[], file: FileItem) => {
    pause()
    const { status, url, response, name, uid } = file
    if (valueType === 'string') {
        if (status === 'error' || (status === 'done' && !response?.data?.url)) {
            fileUrl.value = ''
            previewList.value = []
            Message.error(`${name}上传失败`)
        }
        if (status === 'done' && url) {
            fileUrl.value = url
        }
        if (status === 'done' && response?.data?.url) {
            fileUrl.value = response?.data?.url
        }
    } else {
        if (status === 'error' || (status === 'done' && !response?.data?.url && !url)) {
            const index = fileList?.findIndex?.(item => item.uid === uid)
            Message.error(`${name}上传失败`)
            fileList.splice(index, 1)
        }
        fileUrl.value = fileList.map(item => {
            const { status, response } = item
            if (status === 'done' && response?.data?.url) return response?.data?.url
            if (status === 'done' && item.url && !item.file) return item.url
        })
    }
    await nextTick()
    resume()
}

/**
 * 上传文件前的钩子函数
 *
 * @param file 要上传的文件
 */
const onBeforeUpload = (file: File): Promise<boolean> => {
    return new Promise(resolve => {
        const size = file.size / 1024 / 1024
        if (size > maxSize) {
            Message.error(`图片大小不能超过${maxSize}MB`)
            resolve(false)
        } else {
            resolve(true)
        }
    })
}

const handleExceedLimit = () => Message.error(`最多只能上传${limit}`)

/**
 * 处理预览操作
 *
 * @param file 文件项
 */
const handlePreview = (file: FileItem) => {
    prview({
        src: file.url || file.response?.data?.url,
        list: fileUrl.value as string[]
    })
}

/**
 * 处理拖拽结束事件
 */
const handleDragEnd = async () => {
    fileUrl.value = previewList.value.map(item => item.url || item.response?.data?.url)
}
</script>

<style scoped>
:deep(.arco-upload-list-picture-mask) {
    cursor: move;
}
</style>
